var MouseGesture = new Class({

    Implements: [Events, Options],

    options: {
        xSnap: 30,
        ySnap: 30,
        pointerTraces: false,
        gridPoint: false
    },

    initialize: function (elm, options){
        this.setOptions(options);
        this.elm = $(elm);
        this.elm.store("Gesture", this);
        this.points = [];
        this.startListening();
        if(this.options.pointerTraces){
            this.marker = new Element("span.GestureMove");
        }
    },

    // Events binding
    startListening: function () {
        this.elm.addEvent('mousedown',  MouseGesture.prototype.mouseDownHandler);
        this.elm.addEvent('touchstart', MouseGesture.prototype.mouseDownHandler);
        this.elm.addEvent('mouseup',    MouseGesture.prototype.mouseUpHandler);
        this.elm.addEvent('touchend',   MouseGesture.prototype.mouseUpHandler);
    },

    mouseDownHandler: function (e) {
        e.stop();
        var t = $(e.target);
        t.addEvent("mousemove", MouseGesture.prototype.track);
        t.addEvent("touchmove", MouseGesture.prototype.track);
    },

    mouseUpHandler: function (e) {
        e.stop();
        var t = $(e.target);
        t.removeEvent("mousemove", MouseGesture.prototype.track);
        t.removeEvent("touchmove", MouseGesture.prototype.track);
        t.retrieve("Gesture").parse();
    },

    track: function (e) {
        e.stop();
        var t = $(e.target);
        var o = t.retrieve("Gesture");
        if(!o) return;
        o.points.push(e.client);
        if(o.options.pointerTraces){o.marker.clone(true, false).inject(document.body).setStyles({left:e.client.x, top:e.client.y, "z-index": -1});}
    },

    parse: function () {
        // create utils vars
        var pt, prev, breakpoints1 = [], breakpoints2 = [],last, _self = this;



        // create a Point object
        function setPoint(cur, prev){
            var pt = {};
            if(!cur) return {};
            pt.x = cur.x;
            pt.y = cur.y;
            pt.wayX = (cur.x - prev.x) / Math.abs(cur.x - prev.x) || 0;
            pt.wayY = (cur.y - prev.y) / Math.abs(cur.y - prev.y) || 0;
            pt.cooX = (!pt.wayX) ? "" : (pt.wayX > 0 ? "E" : "W");
            pt.cooY = (!pt.wayY) ? "" : (pt.wayY > 0 ? "S" : "N");
            pt.cardinals = pt.cooY + pt.cooX;
            return pt;
        }

        // check if point is not to close from previous (lighter array)
        function isNotToClose(cur, prev){
            return (pt.wayX && Math.abs(pt.x-last.x) >= _self.options.xSnap) || (pt.wayY && Math.abs(pt.y-last.y) >= _self.options.ySnap);
        }

        // if x and/or y properties changed
        function isChangingDirection (cur, prev){
            return (pt.wayX != prev.wayX || pt.wayY != prev.wayY);
        }

        // if the point is really move (different of x=0 && y=0)
        function isReallyMove(cur){
            return !(!pt.wayX && !pt.wayY)
        }


        // first turn, initialize vars
        // find the breakpoints
        for(var i = 0, l=this.points.length;i<l;i++){
            // first point is a breakpoint
            if(i==0) {
                pt = setPoint(this.points[1], this.points[0]);
                breakpoints1.push(pt);
                continue;
            }


            // set utils variables
            prev = pt;
            pt = setPoint(this.points[i], prev);
            last = breakpoints1.getLast();

            // break detection
            if(
                isReallyMove(pt)
                &&
                isChangingDirection(pt, prev)
                &&
                isNotToClose(pt, prev)
            ){
                breakpoints1.push(pt);
            }
        }

        // align on the grid, simplify the gesture
        for(var i = 0, l=breakpoints1.length;i<l;i++){
            // first point is the reference point
            if(i==0) {
                pt = breakpoints1[0];
                breakpoints2.push(pt);
                continue;
            }


            // set utils variables
            prev = pt;
            pt = breakpoints1[i];
            last = breakpoints2.getLast();

            // grid alignment x
            if(Math.abs(pt.x - last.x) < this.options.xSnap){
                pt.x = last.x;
            }
            // grid alignment y
            if(Math.abs(pt.y - last.y) < this.options.ySnap){
                pt.y = last.y;
            }

            // reset pt
            pt = setPoint(pt, prev);
            
            //
            if(
                isNotToClose(pt, prev)
            ){
                breakpoints2.push(pt);
            }
        }
        
        if(this.options.gridPoint){
            // vizualizazion
            var self = this;
            for (var i = 0; i < breakpoints1.length; i++) {
                var p = breakpoints1[i];
                if(!p) continue;

                new Element("span.point2").inject(document.body).setStyles({
                    left: p.x,
                    top: p.y
                }).set("text", i);
            }
        }

        this.elm.fireEvent(this.buildEvent(breakpoints2));

        // RAZ
        this.points = [];
    },

    buildEvent: function (datas){
        var cards = [];
        datas.shift();
        for (var i = 0, item; i < datas.length; i++) {
            item = datas[i];
            if(cards.getLast() != item.cardinals) cards.push(item.cardinals);
        }
        var evt = "onGesture" + cards.join("-");
        $("evt").set('html', evt)
        return evt;
    },

    reset: function (){
        $$('.point, .GestureMove, .point2').destroy();
        $("evt").set("html","");
    }
});

addEvent("domready", function () {
    var z = $('gestureZone');
    new MouseGesture(z, {
        gridPoint: true,
        pointerTraces: true
    });
    z.addEvent("onGestureW", function (){
        alert("A l'ouest !")
    })
    z.addEvent("onGestureW-S", function (){
        alert("A l'ouest et au Sud !")
    })
    z.addEvent("onGestureE-N", function (){
        alert("A l'est et au Ch'Nord !")
    })
    z.addEvent("onGestureE-S-E", function (){
        alert("Snake !")
    })
    z.addEvent("onGestureS", function (){
        alert("Cap au Sud !")
    })
    z.addEvent("onGestureNE-SE", function (){
        alert("^")
    })
    z.addEvent("onGestureE-SW-E", function (){
        alert("Zorro !")
    })
});